#include <thread>

#include "bindings/bindings_renderer.h"
#include "helpers/general.h"
#include "helpers/resource.h"
#include "models/renderer.h"
#include "models/worker_impl.h"
#include "refs.h"

namespace ohos_rive {
int64_t GetRenderer(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1];
    napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    if (status != napi_ok || argc < 1) {
        LOGE("Failed to get callback info for GetRenderer");
        return 0;
    }

    int64_t rendererPtr;
    status = napi_get_value_int64(env, args[0], &rendererPtr);
    if (status != napi_ok || rendererPtr <= 0) {
        LOGE("Failed to get value for GetRenderer");
        return 0;
    }
    return rendererPtr;
}

napi_value RendererConstructor(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_get_undefined(env, &result);

    size_t argc = 3;
    napi_value args[3] = {nullptr};
    napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    if (status != napi_ok || argc < 3) {
        LOGE("Failed to parse arguments for RendererConstructor");
        return result;
    }

    bool trace = false;
    status = napi_get_value_bool(env, args[1], &trace);
    if (status != napi_ok) {
        LOGE("Get trace parameter failed for RendererConstructor");
        return result;
    }

    RendererType type = RendererType::Rive;
    int typeValue = 0;
    status = napi_get_value_int32(env, args[2], &typeValue);
    if (status == napi_ok) {
        type = static_cast<RendererType>(typeValue);
    } else {
        LOGE("Get type parameter failed for RendererConstructor");
        return result;
    }

    auto *renderer = new Renderer(env, args[0], trace, type);
    if (!renderer) {
        LOGE("Failed to create Renderer instance for RendererConstructor");
        return result;
    }

    RendererType actualType = renderer->GetRendererType();
    if (type != actualType) {
        auto ktRendererClass = GetObjectClass(env, args[0]);
        GetObjectFromMethod(env, ktRendererClass.get(), "setRendererType", actualType);
    }

    napi_value rendererRef;
    status = napi_create_int64(env, reinterpret_cast<int64_t>(renderer), &rendererRef);
    if (status != napi_ok) {
        LOGE("Failed to create napi value for renderer");
        delete renderer;
        return result;
    }
    return rendererRef;
}

napi_value RendererCppDelete(napi_env env, napi_callback_info info)
{
    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (renderer) {
        renderer->ScheduleDispose();
    }

    napi_value result;
    napi_get_undefined(env, &result);
    return result;
}

napi_value RendererCppStop(napi_env env, napi_callback_info info)
{
    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (renderer) {
        renderer->Stop();
    }

    napi_value result;
    napi_get_undefined(env, &result);
    return result;
}

napi_value RendererCppStart(napi_env env, napi_callback_info info)
{
    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (renderer) {
        renderer->Start();
    }

    napi_value result;
    napi_get_undefined(env, &result);
    return result;
}

napi_value RendererCppDoFrame(napi_env env, napi_callback_info info)
{
    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (renderer) {
        renderer->DoFrame();
    }

    napi_value result;
    napi_get_undefined(env, &result);
    return result;
}

napi_value RendererCppSetSurface(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_get_undefined(env, &result);

    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (!renderer) {
        return result;
    }

    napi_status status;
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    if (status != napi_ok || argc < 2) {
        LOGE("Failed to parse arguments for RendererCppSetSurface");
        return result;
    }

    if (renderer->GetRendererType() != RendererType::Canvas) {
        int64_t surfaceId;
        bool lossless = true;
        status = napi_get_value_bigint_int64(env, args[1], &surfaceId, &lossless);
        if (status != napi_ok) {
            LOGE("Failed to get surfaceId for RendererCppSetSurface");
            return result;
        }

        OHNativeWindow *nativeWindow;
        LOGE("surfaceId %{public}ld", surfaceId);
        auto ret = OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow);
        if (ret != 0) {
            LOGE("Failed to create OHNativeWindow for RendererCppSetSurface");
        }

        SurfaceVariant surfaceVariant = reinterpret_cast<EGLNativeWindowType>(nativeWindow);
        renderer->SetSurface(surfaceVariant);
        if (nativeWindow) {
            OH_NativeWindow_DestroyNativeWindow(nativeWindow);
        }
    } else {
        napi_ref surfaceRef;
        status = napi_create_reference(env, args[1], 1, &surfaceRef);
        if (status != napi_ok) {
            LOGE("Failed to create reference for RendererCppSetSurface");
            return result;
        }
        SurfaceVariant surfaceVariant = surfaceRef;
        renderer->SetSurface(surfaceVariant);
    }

    return result;
}

napi_value RendererCppDestroySurface(napi_env env, napi_callback_info info)
{
    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (renderer) {
        SurfaceVariant surfaceVariant = std::monostate{};
        renderer->SetSurface(surfaceVariant);
    }

    napi_value result;
    napi_get_undefined(env, &result);
    return result;
}

napi_value RendererCppSave(napi_env env, napi_callback_info info)
{
    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (renderer) {
        renderer->GetRendererOnWorkerThread()->save();
    }

    napi_value result;
    napi_get_undefined(env, &result);
    return result;
}

napi_value RendererCppRestore(napi_env env, napi_callback_info info)
{
    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (renderer) {
        renderer->GetRendererOnWorkerThread()->restore();
    }

    napi_value result;
    napi_get_undefined(env, &result);
    return result;
}

napi_value RendererCppAlign(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_get_undefined(env, &result);

    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (!renderer) {
        return result;
    }

    napi_status status;
    size_t argc = 6;
    napi_value args[6] = {nullptr};
    status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    if (status != napi_ok || argc < 6) {
        LOGE("Failed to parse arguments for RendererCppAlign");
        return result;
    }

    rive::Fit fit = GetFit(env, args[1]);
    rive::Alignment alignment = GetAlignment(env, args[2]);
    rive::AABB targetBounds = RectFToAABB(env, args[3]);
    rive::AABB sourceBounds = RectFToAABB(env, args[4]);

    double scaleFactor = 0.0f;
    status = napi_get_value_double(env, args[5], &scaleFactor);
    if (status != napi_ok) {
        LOGE("Failed to parse scaleFactor for RendererCppAlign");
        return result;
    }

    renderer->GetRendererOnWorkerThread()->align(fit, alignment, targetBounds, sourceBounds, scaleFactor);

    return result;
}

napi_value RendererCppTransform(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_get_undefined(env, &result);

    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (!renderer) {
        return result;
    }

    napi_status status;
    size_t argc = 7;
    napi_value args[7] = {nullptr};
    status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    if (status != napi_ok || argc < 7) {
        LOGE("Failed to parse arguments for RendererCppTransform");
        return result;
    }

    double x = 0.0f;
    status = napi_get_value_double(env, args[1], &x);
    if (status != napi_ok) {
        LOGE("Failed to parse x for RendererCppTransform");
        return result;
    }
    double sy = 0.0f;
    status = napi_get_value_double(env, args[2], &sy);
    if (status != napi_ok) {
        LOGE("Failed to parse sy for RendererCppTransform");
        return result;
    }
    double sx = 0.0f;
    status = napi_get_value_double(env, args[3], &sx);
    if (status != napi_ok) {
        LOGE("Failed to parse sx for RendererCppTransform");
        return result;
    }
    double y = 0.0f;
    status = napi_get_value_double(env, args[4], &y);
    if (status != napi_ok) {
        LOGE("Failed to parse y for RendererCppTransform");
        return result;
    }
    double tx = 0.0f;
    status = napi_get_value_double(env, args[5], &tx);
    if (status != napi_ok) {
        LOGE("Failed to parse tx for RendererCppTransform");
        return result;
    }
    double ty = 0.0f;
    status = napi_get_value_double(env, args[6], &ty);
    if (status != napi_ok) {
        LOGE("Failed to parse ty for RendererCppTransform");
        return result;
    }

    renderer->GetRendererOnWorkerThread()->transform(rive::Mat2D(x, sy, sx, y, tx, ty));
    return result;
}

napi_value RendererCppWidth(napi_env env, napi_callback_info info)
{
    napi_value result;
    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (renderer) {
        int width = renderer->GetWidth();
        napi_create_int32(env, width, &result);
        return result;
    }

    napi_get_undefined(env, &result);
    return result;
}

napi_value RendererCppHeight(napi_env env, napi_callback_info info)
{
    napi_value result;
    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (renderer) {
        int height = renderer->GetHeight();
        napi_create_int32(env, height, &result);
        return result;
    }

    napi_get_undefined(env, &result);
    return result;
}

napi_value RendererCppAvgFps(napi_env env, napi_callback_info info)
{
    napi_value result;
    auto rendererPtr = GetRenderer(env, info);
    auto *renderer = reinterpret_cast<Renderer *>(rendererPtr);
    if (renderer) {
        double fps = renderer->AverageFps();
        napi_create_double(env, fps, &result);
        return result;
    }

    napi_get_undefined(env, &result);
    return result;
}
} // namespace ohos_rive